package com.winning.vsts.merge.service.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.winning.vsts.merge.service.constant.ApiUrlEnum;
import com.winning.vsts.merge.service.constant.BranchNameEnum;
import com.winning.vsts.merge.service.constant.BranchTypeEnum;
import com.winning.vsts.merge.service.domain.VstsProject;
import com.winning.vsts.merge.service.entity.PullRequestDO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.jackson.JsonObjectDeserializer;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * vsts 客户端
 *
 * @author xcl <xcl@winning.com.cn>
 * @date 2021/11/17
 */
public class VstsClient {

    private final static Logger logger = LoggerFactory.getLogger(VstsClient.class);

    private final static Pattern PATTERN = Pattern.compile("#(\\d+)");


    public String getRepository(RestTemplate restTemplate, VstsProject vstsProject) {
        String repositoryUrl = getRepositoryUrl(vstsProject);
        return restTemplate.getForObject(repositoryUrl, String.class);
    }


    public JSONObject getWorkItem(RestTemplate restTemplate, String workItemId) {
        final String worksItemUrl = getWorksItemUrl(workItemId);
        return restTemplate.getForObject(worksItemUrl, JSONObject.class);
    }

    public JSONObject getWorkItem2(RestTemplate restTemplate, String workItemId) {
//        final String worksItemUrl = getWorksItemUrlById(workItemId);
        return restTemplate.getForObject("", JSONObject.class);
    }

    public JSONObject AnnotatedTags(RestTemplate restTemplate, VstsProject vstsProject,String tagName) {
        String commitId = getCommitIdByBranch(restTemplate, vstsProject, BranchNameEnum.RC.getValue());
        //String commitId = getCommitIdByBranch(restTemplate, vstsProject, "RC_public_pbc");
        String repositoryUrl = getAnnotatedTagsUrl(vstsProject);
        JSONObject object = new JSONObject();
        object.put("name",tagName);
        JSONObject taggedObject = new JSONObject();
        taggedObject.put("objectId",commitId);
        object.put("taggedObject",taggedObject);
        object.put("message","根据门诊RC分支生成标签"+tagName);
        return restTemplate.postForObject(repositoryUrl,object,JSONObject.class);
    }

    public void DelelteTag(RestTemplate restTemplate, VstsProject vstsProject, String tagName) {
        Map<String,String> tagObject = getTags(restTemplate,vstsProject,tagName);
        if(Objects.nonNull(tagObject)){
            String repositoryUrl = getDeteteTagsUrl(vstsProject,tagName);//tagObject.get("objectId")
            String url = "/W.in-MVP/_apis/git/repositories/e227fe45-b6c2-4ed2-ab17-2d4d122d4c42/refs?api-version=4.1-preview.1&&filter=tags%2FV6.0-20230802-it";
            //String url = "/W.in-MVP/_apis/git/repositories/e227fe45-b6c2-4ed2-ab17-2d4d122d4c42/refs?api-version=4.1-preview.1";
            JSONObject object = new JSONObject();
            object.put("name","refs/tags/test");
            object.put("newObjectId","0000000000000000000000000000000000000000");
            object.put("oldObjectId","c6476991db91fef5e69e7bca12a019e8c5561390");
            object.put("refUpdates","c6476991db91fef5e69e7bca12a019e8c5561390");
            restTemplate.patchForObject(repositoryUrl,object,JSONObject.class);
        }

    }

    public Boolean checkTag(RestTemplate restTemplate, VstsProject vstsProject, String tagName) {
        String commitId = getCommitIdByTag(restTemplate, vstsProject, tagName);
        if(StringUtils.isEmpty(commitId)) return false;
        return true;
    }

    public Boolean checkLastCommitHaveTag(RestTemplate restTemplate, VstsProject vstsProject, String branchName) {
        String commitId = getCommitIdByBranch(restTemplate, vstsProject, branchName);
        //String queryCommitUrl = getSingleAnnotatedtagsUrl(vstsProject,commitId);//getCommitsUrl(vstsProject,branchName); //
        Map<String,String> tagObject = getTags(restTemplate,vstsProject,branchName);
        if(Objects.nonNull(tagObject)){
            if(tagObject.containsKey("resolvedCommitId")&&tagObject.get("resolvedCommitId").equals(commitId)){
                return true;
            }
        }
        return null;
    }

    public Boolean checkByTag(RestTemplate restTemplate, VstsProject vstsProject, String tagName) {
        //String queryCommitUrl = getSingleAnnotatedtagsUrl(vstsProject,commitId);//getCommitsUrl(vstsProject,branchName); //
        Map<String,String> tagObject = getTags(restTemplate,vstsProject,tagName);
        if(Objects.nonNull(tagObject)){
            return true;
        }
        return false;
    }

    public Map<String,String> getTags(RestTemplate restTemplate,VstsProject vstsProject,String tagName){
        String tagUrl = getTagsUrl(vstsProject);
        JSONObject tagObject = restTemplate.getForObject(tagUrl,JSONObject.class);
        JSONArray array =  (JSONArray)tagObject.getJSONObject("dataProviderData").getJSONObject("data").getJSONObject("ms.vss-code-web.git-tags-data-provider").getJSONArray("tags");
        for (Object o : array) {
            Map<String,String> tag = (Map<String,String>)o;
            if(tag.containsKey("name")&&tag.get("name").equals(tagName)){
                return tag;
            }
        }
        return null;
    }

    private String getTagsUrl(VstsProject vstsProject) {
        return String.format("/%s%s/%s/tags?_xhr=true",
                vstsProject.getProjectOrgan(),
                ApiUrlEnum.TAG.getUrl(), vstsProject.getProjectName());
    }

    public void abandoned(RestTemplate restTemplate, VstsProject vstsProject, String pullRequestId) {
        JSONObject abandonedRequest = new JSONObject();
        abandonedRequest.put("status", "abandoned");
        String abandonedPrUrl = getPullRequestIdUrl(vstsProject, pullRequestId);
        String patchForObject = restTemplate.patchForObject(abandonedPrUrl, abandonedRequest, String.class);
        logger.info("项目:{}放弃PR返回{}", vstsProject.getProjectName(), patchForObject);
    }

    private String getPullRequestIdUrl(VstsProject vstsProject, String pullRequestId) {
        return String.format("/%s%s/%s/pullrequests/%s?api-version=4.1",
            vstsProject.getProjectOrgan(),
            ApiUrlEnum.REPOSITORY.getUrl(), vstsProject.getProjectName(), pullRequestId);
    }


    public void autoApprove(RestTemplate restTemplate, VstsProject vstsProject, String pullRequestId,
                            String userId) {
        JSONObject checkRequest = new JSONObject();
        checkRequest.put("vote", 10);
        HttpEntity<JSONObject> httpEntity = new HttpEntity<>(checkRequest);
        String autoApprovalUrl = getAutoApproveUrl(vstsProject, pullRequestId, userId);
        // 自动审批
        ResponseEntity<String> exchange = restTemplate.exchange(autoApprovalUrl,
            HttpMethod.PUT, httpEntity, String.class);
        logger.info("项目:{}自动审批返回{}", vstsProject.getProjectName(), exchange.getBody());
    }

    private String getAutoApproveUrl(VstsProject vstsProject, String pullRequestId, String userId) {
        return String.format("/%s%s/%s/pullrequests/%s/reviewers/%s?api-version=4.1",
            vstsProject.getProjectOrgan(),
            ApiUrlEnum.REPOSITORY.getUrl(), vstsProject.getProjectName(), pullRequestId, userId);
    }


    public void setAutoComplete(RestTemplate restTemplate, VstsProject vstsProject, String pullRequestId, String userId,
                                boolean deleteFlag) {
        JSONObject patchRequest = new JSONObject();
        JSONObject autoCompleteSetBy = new JSONObject();
        autoCompleteSetBy.put("id", userId);
        patchRequest.put("autoCompleteSetBy", autoCompleteSetBy);
        JSONObject completionOptions = new JSONObject();
        completionOptions.put("triggeredByAutoComplete", true);
        completionOptions.put("deleteSourceBranch", deleteFlag);
        completionOptions.put("squashMerge", false);
        patchRequest.put("completionOptions", completionOptions);
        String pullRequestUrl = getPullRequestIdUrl(vstsProject, pullRequestId);
        String patchForObject = restTemplate.patchForObject(pullRequestUrl, patchRequest, String.class);
        logger.info("项目:{}自动完成返回{}", vstsProject.getProjectName(), patchForObject);
    }


    public JSONObject createPullRequest(RestTemplate restTemplate, VstsProject vstsProject, JSONObject request,
                                        Collection<String> workItemIds) {
        if(Objects.nonNull(workItemIds)){
            // 添加工作项
            List<JSONObject> workItemRefs = workItemIds.stream()
                    .filter(StringUtils::hasLength)
                    .map(workItemId -> {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("id", workItemId);
                        return jsonObject;
                    }).collect(Collectors.toList());
            request.put("workItemRefs", workItemRefs);
            request.put("description", "#" + String.join(" #", workItemIds));
        }

        String pullRequestUrl = getPullRequestUrl(vstsProject);
        return restTemplate.postForObject(pullRequestUrl, request, JSONObject.class);
    }

    private String getPullRequestUrl(VstsProject vstsProject) {
        return String.format("/%s%s/%s/pullrequests?supportsIterations=true&api-version=4.1",
            vstsProject.getProjectOrgan(),
            ApiUrlEnum.REPOSITORY.getUrl(), vstsProject.getProjectName());
    }

    private JSONObject createPrParam(String sourceBranch, String targetBranch) {
        JSONObject request = new JSONObject();
        request.put("sourceRefName", "refs/heads/" + sourceBranch);
        request.put("targetRefName", "refs/heads/" + targetBranch);
        request.put("title", "merge " + sourceBranch + " into " + targetBranch);
        return request;
    }


    public String createBranch(RestTemplate restTemplate, VstsProject vstsProject, String sourceBranch,
                               String targetBranch) {
        // 先获取源分支的commitId
        String commitId = getCommitIdByBranch(restTemplate, vstsProject, sourceBranch);
        String otherBranchName = sourceBranch + "_to_" + targetBranch;
        // 修改原分支名称
        String middleBranchName = "middle/" + otherBranchName;
        return createBranchByCommitId(restTemplate, vstsProject, middleBranchName, commitId);
    }


    public void deleteBranch(RestTemplate restTemplate, VstsProject vstsProject, String branch) {
        // 先获取源分支的commitId
        String commitId = getCommitIdByBranch(restTemplate, vstsProject, branch);
        JSONArray array = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        String otherBranch = "refs/heads/" + branch;
        jsonObject.put("name", otherBranch);
        jsonObject.put("oldObjectId", commitId);
        jsonObject.put("newObjectId", "0000000000000000000000000000000000000000");
        array.add(jsonObject);
        String createBranchUrl = getBranchUrl(vstsProject, otherBranch);
        String result = restTemplate.postForObject(createBranchUrl, array, String.class);
        logger.info("项目{}删除分支返回:{}", vstsProject.getProjectName(), result);
    }


    public String createBranchByCommitId(RestTemplate restTemplate, VstsProject vstsProject, String branchName,
                                         String commitId) {
        JSONArray array = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        String otherBranch = "refs/heads/" + branchName;
        jsonObject.put("name", otherBranch);
        jsonObject.put("oldObjectId", "0000000000000000000000000000000000000000");
        jsonObject.put("newObjectId", commitId);
        array.add(jsonObject);
        String createBranchUrl = getBranchUrl(vstsProject, branchName);
        String result = restTemplate.postForObject(createBranchUrl, array, String.class);
        logger.info("项目{}创建分支返回:{}", vstsProject.getProjectName(), result);
        return otherBranch;
    }

    private String getBranchUrl(VstsProject vstsProject, String branchName) {
        return String.format("/%s%s/%s/refs?filter=%s&api-version=4.1", vstsProject.getProjectOrgan(),
            ApiUrlEnum.REPOSITORY.getUrl(), vstsProject.getProjectName(), branchName);
    }


    public List<JSONObject> compareCommitIds(RestTemplate restTemplate, VstsProject vstsProject, String targetBranch,
                                             String sourceBranch) {
        String compareBranchCommitIdUrl = getCompareBranchCommitIdUrl(vstsProject, sourceBranch, targetBranch);
        JSONObject contentObject = restTemplate.getForObject(compareBranchCommitIdUrl, JSONObject.class);
        logger.info("项目{}获取工作项信息:{},返回:{}", vstsProject.getProjectName(), compareBranchCommitIdUrl, contentObject);
        int count = contentObject.getIntValue("count");
        if (count <= 0) {
            return Lists.newArrayList();
        }
        return contentObject.getObject("value", new TypeReference<List<JSONObject>>() {
        }.getType());
    }


    public Set<String> compareItemIds(RestTemplate restTemplate, VstsProject vstsProject, String targetBranch,
                                      String sourceBranch) {
        String compareBranchCommitIdUrl = getCompareBranchCommitIdUrl(vstsProject, sourceBranch, targetBranch);
        JSONObject contentObject = restTemplate.getForObject(compareBranchCommitIdUrl, JSONObject.class);
        int count = contentObject.getIntValue("count");
        if (count <= 0) {
            return Sets.newHashSet();
        }
        List<JSONObject> branches = contentObject.getObject("value", new TypeReference<List<JSONObject>>() {
        }.getType());
        if (CollectionUtils.isEmpty(branches)) {
            return Sets.newHashSet();
        }
        return branches.stream().flatMap(item -> {
            String comment = item.getString("comment");
            // PR信息给去掉
            if (comment.contains("PR")) {
                return Stream.empty();
            }
            Matcher matcher = PATTERN.matcher(comment);
            Set<String> workIds = new HashSet<>();
            while (matcher.find()) {
                workIds.add(matcher.group().trim().substring(1));
            }
            return workIds.stream();
        }).collect(Collectors.toSet());
    }


    public String getCommitIdByBranch(RestTemplate restTemplate, VstsProject vstsProject, String branch) {
        // 获取信息
        String commitIdUrl = getCommitIdUrl(vstsProject, branch, BranchTypeEnum.BRANCH);
        JSONObject contentObject = restTemplate.getForObject(commitIdUrl, JSONObject.class);
        List<JSONObject> branches = contentObject.getObject("value", new TypeReference<List<JSONObject>>() {
        }.getType());
        return branches.get(0).getString("commitId");
    }

    public String getCommitIdByTag(RestTemplate restTemplate, VstsProject vstsProject, String tagName) {
        // 获取信息
        String commitIdUrl = getCommitIdUrl(vstsProject, tagName, BranchTypeEnum.TAG);
        JSONObject contentObject = restTemplate.getForObject(commitIdUrl, JSONObject.class);
        List<JSONObject> branches = contentObject.getObject("value", new TypeReference<List<JSONObject>>() {
        }.getType());
        return branches.get(0).getString("commitId");
    }

    public String getSingleCommitUrl(VstsProject vstsProject,String commitId){
        return String.format(
                "/%s%s/%s/commits/%s/?&api-version=4.1",
                vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
                vstsProject.getProjectName(),commitId);
    }


    private String getCommitIdUrl(VstsProject vstsProject, String branch, BranchTypeEnum branchTypeEnum) {
        return String.format(
            "/%s%s/%s/commits?searchCriteria.itemVersion.version=%s&searchCriteria.itemVersion" +
                ".versionType=%s&$top=1&api-version=4.1",
            vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
            vstsProject.getProjectName(), branch, branchTypeEnum.getType());
    }

    private String getCompareBranchCommitIdUrl(VstsProject vstsProject, String sourceBranch, String targetBranch) {
        return String.format(
            "/%s%s/%s/commits?searchCriteria.itemVersion.version=%s&searchCriteria.itemVersion" +
                ".versionType=branch&searchCriteria.compareVersion.version=%s&searchCriteria.compareVersion" +
                ".versionType=branch&api-version=4.1",
            vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
            vstsProject.getProjectName(), sourceBranch, targetBranch);
    }


    private String getRepositoryUrl(VstsProject vstsProject) {
        return String.format("/%s%s/%s?api-version=4.1", vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
            vstsProject.getProjectName());
    }

    private String getAnnotatedTagsUrl(VstsProject vstsProject){
        return String.format("/%s%s/%s/annotatedtags?api-version=4.1-preview.1", vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
                vstsProject.getProjectName());
    }
    //GET https://dev.azure.com/{organization}/{project}/_apis/git/repositories/{repositoryId}/annotatedtags/{objectId}?api-version=6.0-preview.1
    private String getDeteteTagsUrl(VstsProject vstsProject,String tagName){
        return String.format("/%s%s/%s/annotatedtags/%s?api-version=4.1-preview.1", vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
                vstsProject.getProjectName(),tagName);
    }

    private String getCommitsUrl(VstsProject vstsProject,String branchName){
        return String.format("/%s%s/%s/commits?&searchCriteria.itemVersion.version={searchCriteria.itemVersion.version}&$skip={$top}&$top={$top}&api-version=4.1-preview.1", vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
                vstsProject.getProjectName());
    }

    private String getSingleAnnotatedtagsUrl(VstsProject vstsProject,String commitId){
        return String.format("/%s%s/%s/annotatedtags/%s?api-version=4.1-preview.1", vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
                vstsProject.getProjectName(),commitId);
    }



    private String getWorksItemUrl(String workItemId) {
        return String.format("%s/%s?api-version=4.1&$expand=relations", ApiUrlEnum.WORK_ITEM.getUrl(), workItemId);
    }


    public List<JSONObject> queryBranchPullRequest(RestTemplate restTemplate, VstsProject vstsProject, String branch) {
        String queryPullRequestUrl = getBranchPrUrl(vstsProject, branch);
        JSONObject contentObject = restTemplate.getForObject(queryPullRequestUrl, JSONObject.class);
        int count = contentObject.getIntValue("count");
        if (count <= 0) {
            return Lists.newArrayList();
        }
        return contentObject.getObject("value", new TypeReference<List<JSONObject>>() {
        }.getType());
    }

    private String getBranchPrUrl(VstsProject vstsProject, String branch) {
        String refName = "refs/heads/" + branch;
        return String.format(
            "/%s%s/%s/pullrequests?searchCriteria.targetRefName=%s&searchCriteria.status=active&api-version=4.1",
            vstsProject.getProjectOrgan(), ApiUrlEnum.REPOSITORY.getUrl(),
            vstsProject.getProjectName(), refName);
    }


    public PullRequestDO getPullRequestParam(RestTemplate restTemplate, VstsProject vstsProject, String sourceBranch,
                                             String targetBranch) {
        PullRequestDO pullRequestDO = new PullRequestDO();
        pullRequestDO.setSourceBranch(sourceBranch);
        pullRequestDO.setTargetBranch(targetBranch);
        pullRequestDO.setVstsProject(vstsProject);
        // 首先获取分支差异的commitId信息
        List<JSONObject> commitValues = compareCommitIds(restTemplate, vstsProject, sourceBranch, targetBranch);
        int size = commitValues.size();
        pullRequestDO.setCommitCount(size);
        if (CollectionUtils.isEmpty(commitValues)) {
            pullRequestDO.setPrCommitCount(0);
            return pullRequestDO;
        }
        // 获取PR的commit数量以及工作项ID
        int prCommitCount = 0;
        Set<String> workItemIds = Sets.newHashSetWithExpectedSize(size);
        for (JSONObject commitValue : commitValues) {
            String comment = commitValue.getString("comment");
            // PR信息给去掉
            if ((comment.contains("拉取请求") && comment.contains("合并到")) || comment.contains("PR")) {
                prCommitCount++;
                continue;
            }
            Matcher matcher = PATTERN.matcher(comment);
            while (matcher.find()) {
                workItemIds.add(matcher.group().trim().substring(1));
            }
        }
        pullRequestDO.setPrCommitCount(prCommitCount);
        pullRequestDO.setWorksItemIds(workItemIds);
        pullRequestDO.setRequest(createPrParam(sourceBranch, targetBranch));
        return pullRequestDO;
    }


    public JSONObject queryPullRequest(RestTemplate restTemplate, VstsProject vstsProject, String pullRequestId) {
        String pullRequestIdUrl = getPullRequestIdUrl(vstsProject, pullRequestId);
        JSONObject pullRequestStatus = restTemplate.getForObject(pullRequestIdUrl, JSONObject.class);
        String status = pullRequestStatus.getString("status");
        String mergeStatus = pullRequestStatus.getString("mergeStatus");
        logger.info("项目{}的PR状态:{},合并状态:{}", vstsProject.getProjectName(), status, mergeStatus);
        if ("conflicts".equals(mergeStatus) || "failure".equals(mergeStatus)) {
            // 冲突和失败进行提示
            logger.error("项目{}的PR状态:{},合并状态:{}", vstsProject.getProjectName(), status, mergeStatus);
        }
        return pullRequestStatus;
    }


}
